#include "RenderBufferWrapper.h"
#include <vcclr.h>

using namespace AwesomiumDotNet;
using namespace System::Drawing::Imaging;

RenderBuffer::RenderBuffer(const Awesomium::RenderBuffer* buffer)
{
	readOnly = true;
	_buffer = (Awesomium::RenderBuffer*)buffer;
}

RenderBuffer::RenderBuffer(Awesomium::RenderBuffer* buffer)
{
	readOnly = false;
	_buffer = buffer;
}

RenderBuffer::RenderBuffer(int width, int height)
{
	readOnly = false;
	_buffer = new Awesomium::RenderBuffer(width, height);
}

RenderBuffer::RenderBuffer(IntPtr buffer, int width, int height, int rowSpan, bool autoDeleteBuffer)
{
	readOnly = false;
	_buffer = new Awesomium::RenderBuffer((unsigned char*)buffer.ToPointer(), width, height, rowSpan, autoDeleteBuffer);
}

RenderBuffer::RenderBuffer(IntPtr buffer, int width, int height, int rowSpan)
{
	readOnly = false;
	_buffer = new Awesomium::RenderBuffer((unsigned char*)buffer.ToPointer(), width, height, rowSpan);
}

array<System::Byte>^ RenderBuffer::Copy(int rowSpan, int depth, bool convertToRGBA)
{
	array<System::Byte,1>^ destBuffer = gcnew array<System::Byte,1>(rowSpan * _buffer->height);
	pin_ptr<unsigned char> destPtr = &destBuffer[0];

	_buffer->copyTo(destPtr, rowSpan, depth, convertToRGBA);

	return destBuffer;
}

void RenderBuffer::CopyArea(RenderBuffer^ sourceBuffer, Rectangle^ sourceRect, Rectangle^ destinationRect)
{
	_buffer->copyArea(*sourceBuffer->UnmanagedPointer,
		Awesomium::Rect(sourceRect->X, sourceRect->Y, sourceRect->Width, sourceRect->Height),
		Awesomium::Rect(destinationRect->X, destinationRect->Y, destinationRect->Width, destinationRect->Height)
	);
}

void RenderBuffer::CopyArea(IntPtr sourceBuffer, int sourceRowSpan, Rectangle^ sourceRect, bool forceOpaque)
{
	_buffer->copyArea((unsigned char*)sourceBuffer.ToPointer(), sourceRowSpan,
		Awesomium::Rect(sourceRect->X, sourceRect->Y, sourceRect->Width, sourceRect->Height), forceOpaque);
}

void RenderBuffer::CopyArea(IntPtr sourceBuffer, int sourceRowSpan, Rectangle^ sourceRect)
{
	_buffer->copyArea((unsigned char*)sourceBuffer.ToPointer(), sourceRowSpan,
		Awesomium::Rect(sourceRect->X, sourceRect->Y, sourceRect->Width, sourceRect->Height));
}

void RenderBuffer::CopyFrom(IntPtr sourceBuffer, int sourceRowSpan)
{
	_buffer->copyFrom((unsigned char*)sourceBuffer.ToPointer(), sourceRowSpan);
}

void RenderBuffer::CopyTo(array<System::Byte>^ destinationBuffer, int rowSpan, int depth, bool convertToRGBA)
{
	pin_ptr<unsigned char> destinationPtr = &destinationBuffer[0];
	_buffer->copyTo(destinationPtr, rowSpan, depth, convertToRGBA);
}

void RenderBuffer::CopyTo(IntPtr destinationBuffer, int rowSpan, int depth, bool convertToRGBA)
{
	_buffer->copyTo((unsigned char*)destinationBuffer.ToPointer(), rowSpan, depth, convertToRGBA);
}

bool RenderBuffer::SaveToPng(String^ filePath, bool preserveTransparency)
{
	pin_ptr<const wchar_t> wch = PtrToStringChars(filePath);
	return _buffer->saveToPNG(wch, preserveTransparency);
}

bool RenderBuffer::SaveToPng(String^ filePath)
{
	pin_ptr<const wchar_t> wch = PtrToStringChars(filePath);
	return _buffer->saveToPNG(wch);
}

bool RenderBuffer::SaveToJpeg(String^ filePath)
{
	pin_ptr<const wchar_t> wch = PtrToStringChars(filePath);
	return _buffer->saveToJPEG(wch);
}

bool RenderBuffer::SaveToJpeg(String^ filePath, int quality)
{
	pin_ptr<const wchar_t> wch = PtrToStringChars(filePath);
	return _buffer->saveToJPEG(wch, quality);
}

Bitmap^ RenderBuffer::ToBitmap(int rowSpan, int depth)
{
	PixelFormat pf;
	switch(depth)
	{
	case 3:
		pf = PixelFormat::Format24bppRgb;
		break;
	case 4:
		pf = PixelFormat::Format32bppArgb;
		break;
	default:
		throw gcnew ArgumentException("Bitmap depth can only be 3 or 4, was " + depth.ToString() + ".");
	}

	// Create bitmap
	Bitmap^ bitmap = gcnew Bitmap(_buffer->width, _buffer->height, pf);

	BitmapData^ data = bitmap->LockBits(
		Rectangle(0,0, _buffer->width, _buffer->height),
		ImageLockMode::WriteOnly, bitmap->PixelFormat
	);

	_buffer->copyTo((unsigned char*)data->Scan0.ToPointer(), rowSpan, depth, false);
	bitmap->UnlockBits(data);

	return bitmap;
}

void RenderBuffer::ToBitmap(Bitmap^ bitmap)
{
	int depth;
	switch (bitmap->PixelFormat)
	{
	case PixelFormat::Format24bppRgb:
		depth = 3;
		break;
	case PixelFormat::Format32bppArgb:
	case PixelFormat::Format32bppRgb:
		depth = 4;
		break;
	default:
		throw gcnew Exception("Invalid bitmap pixel format.");
	}

	if (_buffer->width != bitmap->Width || _buffer->height != bitmap->Height)
		throw gcnew ArgumentException("Bitmap size must match the WebView size.");

	BitmapData^ data = bitmap->LockBits(
		Rectangle(0,0, _buffer->width, _buffer->height),
		ImageLockMode::WriteOnly, bitmap->PixelFormat
	);

	_buffer->copyTo((unsigned char*)data->Scan0.ToPointer(), _buffer->width*depth, depth, false);
	bitmap->UnlockBits(data);
}

unsigned char RenderBuffer::GetAlphaAtPoint(int x, int y)
{
	return _buffer->getAlphaAtPoint(x, y);
}

void RenderBuffer::Reserve(int width, int height)
{
	_buffer->reserve(width, height);
}

void RenderBuffer::ScrollArea(int dx, int dy, Rectangle^ clipRect)
{
	_buffer->scrollArea(dx, dy,
		Awesomium::Rect(clipRect->X, clipRect->Y, clipRect->Width, clipRect->Height));
}

IntPtr RenderBuffer::Buffer::get()
{
	return IntPtr(_buffer->buffer);
}
void RenderBuffer::Buffer::set(IntPtr value)
{
	if (readOnly == false)
		_buffer->buffer = (unsigned char*)value.ToPointer();
}

int RenderBuffer::Width::get()
{
	return _buffer->width;
}
void RenderBuffer::Width::set(int value)
{
	if (readOnly == false)
		_buffer->width = value;
}

int RenderBuffer::Height::get()
{
	return _buffer->height;
}
void RenderBuffer::Height::set(int value)
{
	if (readOnly == false)
		_buffer->height = value;
}

int RenderBuffer::RowSpan::get()
{
	return _buffer->rowSpan;
}
void RenderBuffer::RowSpan::set(int value)
{
	if (readOnly == false)
		_buffer->rowSpan = value;
}

bool RenderBuffer::OwnsBuffer::get()
{
	return _buffer->ownsBuffer;
}
void RenderBuffer::OwnsBuffer::set(bool value)
{
	if (readOnly == false)
		_buffer->ownsBuffer = value;
}

bool RenderBuffer::ReadOnly::get()
{
	return readOnly;
}

const Awesomium::RenderBuffer* RenderBuffer::UnmanagedPointer::get()
{
	return _buffer;
}
